
package com.x52im.rbchat;

import android.app.Application;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import com.eva.android.widget.WidgetUtils;
import com.x52im.mall.MallGlobal;
import com.x52im.mall.MallGlobalImpl;
import com.x52im.rbchat.cache.FriendsReqProvider;
import com.x52im.rainbowchat.http.logic.dto.RosterElementEntity;
import com.x52im.rbchat.cache.AlarmsProvider;
import com.x52im.rbchat.logic.chat_friend.FriendChattingActivity;
import com.x52im.rbchat.cache.FriendsListProvider;
import com.x52im.rbchat.cache.MessagesProvider;
import com.x52im.rbchat.logic.chat_friend.vv.IVideoActivity;
import com.x52im.rbchat.logic.chat_friend.vv.VVP2PProvider;
import com.x52im.rbchat.logic.chat_group.GroupChattingActivity;
import com.x52im.rbchat.cache.GroupsProvider;
import com.x52im.rbchat.logic.chat_guest.TempChattingActivity;
import com.x52im.rbchat.cache.GroupsMessagesProvider;
import com.x52im.rbchat.logic.chat_group.impl.ProhibitSpeechCheckerProvider;
import com.x52im.rbchat.cache.FaceDataProvider;
import com.x52im.rbchat.logic.chat_root.impl.MessageRevokingManager;
import com.x52im.rbchat.logic.main.LoginActivity;
import com.x52im.rbchat.network.http.bigfile.BigFileDownloadManager;
import com.x52im.rbchat.network.http.bigfile.BigFileUploadManager;
import com.x52im.rbchat.network.im.ChatBaseEventListener;
import com.x52im.rbchat.network.im.ChatMessageEventListener;
import com.x52im.rbchat.network.im.MessageQoSListener;

import net.x52im.mobileimsdk.android.ClientCoreSDK;
import net.x52im.mobileimsdk.android.conf.ConfigEntity;
import net.x52im.mobileimsdk.android.core.LocalDataSender;
import net.x52im.mobileimsdk.android.core.LocalSocketProvider;
import net.x52im.mobileimsdk.android.utils.MBThreadPoolExecutor;

import java.util.Observer;

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;

/**
 * RainbowChat中的IM相关数据模型、全局变量等管理类.
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @since 1.0
 */
public class IMClientManager implements MallGlobal.RobotimeMallProvider {

	private static String TAG = IMClientManager.class.getSimpleName();

	/** 全局Application对象引用 */
	private final Application applicationContext;
	
	/** 
	 * MobileIMSDK的基础通信消息的回调事件实现类（回调事件可以是：登陆成功事件 通知、掉线事件通知等）。
	 * <p> 
	 * 通过 ClientCoreSDK.setChatBaseEvent(..) 方法设置之，可实现回调事件的通知和处理。
	 */
	private ChatBaseEventListener baseEventListener = null;
	
	/**
	 * MobileIMSDK的通用数据通信消息的回调事件接口（回调事件可以是：收到聊天数据事件 通知、服务端返回的
	 * 错误信息事件通知等）。 
	 * <p>
  	 * 通过 ClientCoreSDK.setChatTransDataEvent(..) 方法设置之，可实现回调事件的通知和处理。
	 */
	private ChatMessageEventListener transDataListener = null;
	
	/**
	 * MobileIMSDK的QoS质量保证机制的回调事件实现类。
	 * <p> 
	 * 通过 ClientCoreSDK.setMessageQoSEvent(..) 方法设置之，可实现消息已被收到或未成功送出的通知和处理。
	 */
	private MessageQoSListener messageQoSListener = null;
	
	/**
	 * 当前登陆用户的个人信息全局对象。
	 * 本对象在用户登陆成功后被设置，后绪的个人信息显示、更新等统一使用本对象来完成即可。
	 */
	private RosterElementEntity localUserInfo = null;

	/** 一对一聊天(含好友聊天、陌生人聊天)消息数据提供者(key=uid，value=与每个好友或陌生人的消息集合)。 */
	private MessagesProvider messagesProvider = null;
	/** 普通群聊/世界频道聊天消息的数据提供者(key=gid，value=与每个群的消息集合)*/
	private GroupsMessagesProvider groupsMessagesProvider = null;
	/** 首页“消息”提示信息的数据提供者 */
	private AlarmsProvider alarmsProvider = null;
    /** "我"的好友列表数据提供者 */
    private FriendsListProvider friendsListProvider = null;
    /** "我"的群组信息列表数据提供者 */
    private GroupsProvider groupsProvider = null;
	/** 表情数据提供者 */
	private FaceDataProvider faceDataProvider = null;
	/** 加好友请求数据提供者 */
	private FriendsReqProvider friendsReqProvider = null;

	/** 大文件上传管理器 */
	private BigFileUploadManager bigFileUploadManager = null;
	/** 大文件下载管理器 */
	private BigFileDownloadManager bigFileDownloadManager = null;
	/** 消息"撤回"管理器 */
	private MessageRevokingManager messageRevokingManager = null;

	// ******************************************************************* For 商城 START
	/** RainbowChat商城中要使用到的一些配置、全局变量等封装对象 */
	private MallGlobal robotimeMall = null;
	// ******************************************************************* For 商城 END

	/** 
	 * 当前正在聊天中的用户UID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link FriendChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link FriendChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）
	 */
	private String currentFrontChattingUserUID = null;
    /**
     * 当前正在临时聊天中的用户UID.
     * <p>
     * <b>重要说明：</b>此变量只在{@link TempChattingActivity}处于前景（即在onResume()方法调用的情况下）被
     * 设置、在{@link TempChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）
     */
    // 不与currentFrontChattingUserUID共用一个表示处于聊天界面时，是因为正式聊天和临时聊天是
    // 2个界面，正好处于正式聊天也不意味着临时聊天进行中，所以不能撒（相反也一样）
    private String currentFrontTempChattingUserUID = null;
    /**
     * 当前正在群组聊天中的groupId.
     * <p>
     * 目前其实没有完全实现群组聊天功能，但BBS功能是群组聊天的前身，目前为了实现BBS功能已把群组聊天的有些基础设施部分实现了.
     * 目前所叫的“群组”聊天实际是可以理解为BBS专用聊天，而非完全的群婚后（多群）聊天功能哦。
     * <p>
     * <b>重要说明：</b>此变量只在{@link GroupChattingActivity}处于前景（即在onResume()方法调用的情况下）被
     * 设置、在{@link TempChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）
     *
     */
    private String currentFrontGroupChattingGroupID = null;
	
	/** 当音视频聊天时把该activity引用存起来备用 */
	// FIXME：此引用暂时并非最佳实践，还有优化余地，日后再作极致优化哦
	private IVideoActivity currentVideoChattingActivity = null;

	/**
	 * true表示个人相册管理界面中在onResume时需要刷新界面数据，否则不需要.
	 *
	 * 说明：因上相片的预览和上传是处于相机等界面之后的Activity里，它不能通过回调（因为隔了系统的相机结果预览界面之后
	 * ，此系统界面当然不能让它传递回调结果），但有一个方法：可以像聊天时的图片消息一样上传界面里只需把数据放到一个全局
	 * 数据结构中，在ListView里getView时来异步处理之，但这需要独立的数据结构：不现实也不合适。
	 *
	 * 综上：目前相片上传成功后设置此标识为true，当相片管理界面onResume时从服务端刷新1次数据从而保证紧随新相片数据能
	 * 在本地看见的处理方法是当前能使用的较佳方式.
	 *  */
	private boolean needRefreshPhotoListForUpdate = false;
	
	/* MobileIMSDK是否已被初始化. true表示已初化完成，否则未初始化. */
	private boolean _init;

	/*
	 * 优先要做的设置可以放在类方法中
	 */
	static {
		// 显示的toast级别（通过或运算控制不必要的Toast显示，从或运算中去掉一个类型就表示不显示该类型的Toast）
		WidgetUtils.toastTypeSurport = WidgetUtils.TOAST_TYPE_NONE
				| WidgetUtils.TOAST_TYPE_INFO | WidgetUtils.TOAST_TYPE_OK
				| WidgetUtils.TOAST_TYPE_WARN | WidgetUtils.TOAST_TYPE_ERROR;
	}

	/**
	 * 构造方法。
	 *
	 * @param applicationContext 全局Application对象引用
	 */
	public IMClientManager(Application applicationContext) {
		this.applicationContext = applicationContext;
		init();
	}
	
	private void init() {
		// 设置IM聊天服务端IP地址或域名
		ConfigEntity.serverIP = Const.IM_SERVER_IP;
		// 设置IM聊天服务端的监听端口（不设置则默认是9903）
		ConfigEntity.serverPort = Const.IM_SERVER_PORT;
//      // 设置本地客户端的网络兼听端口，此端口可随便设置，只要不与其它手机端程
//      // 序端口冲突即可（不设置则默认是8901，如果设置为0则表示由系统自动分配端口）
//      ConfigEntity.localPort = 8901;
		
		// 初始化
		initMobileIMSDK();
	}
	
	/**
	 * IM框架初始化方法，本方法在连接IM服务器前必须被调用1次，否则IM底层框架将无法工作。
	 */
	public void initMobileIMSDK() {
		if(!this._init) {
			baseEventListener = new ChatBaseEventListener(applicationContext);
			transDataListener = new ChatMessageEventListener(applicationContext);
			messageQoSListener = new MessageQoSListener(applicationContext);

			ClientCoreSDK.getInstance().init(applicationContext);
			ClientCoreSDK.getInstance().setChatBaseEvent(baseEventListener);
			ClientCoreSDK.getInstance().setChatMessageEvent(transDataListener);
			ClientCoreSDK.getInstance().setMessageQoSEvent(messageQoSListener);

			// MobileIMSDK核心IM框架的敏感度模式设置
			ConfigEntity.setSenseMode(ConfigEntity.SenseMode.MODE_5S);

			// 开始/关闭SDK的Debug信息
			ClientCoreSDK.DEBUG = false;// true

			// 设置最大TCP帧内容长度（不设置则默认最大是 6 * 1024字节）
//			LocalSocketProvider.TCP_FRAME_MAX_BODY_LENGTH = 60 * 1024;

			// 开启SSL/TLS加密传输（请确保服务端也已开启SSL）
//			LocalSocketProvider.sslContext = createSslContext();

			friendsListProvider = new FriendsListProvider();
			groupsProvider = new GroupsProvider();
			messagesProvider = new MessagesProvider();
			groupsMessagesProvider = new GroupsMessagesProvider();
			alarmsProvider = new AlarmsProvider(applicationContext);
			friendsReqProvider = new FriendsReqProvider();

			// 实例化大文件上传任务管理器
			bigFileUploadManager = new BigFileUploadManager(applicationContext);//, null);
			// 实例化大文件下载任务管理器
			bigFileDownloadManager = new BigFileDownloadManager(applicationContext);
			// 实例化消息"撤回"管理器
			messageRevokingManager = new MessageRevokingManager();

			// ******************************************************************* For Mall START
			robotimeMall = new MallGlobalImpl(Const.HTTP_COMMON_CONTROLLER_URL);
			// ******************************************************************* For Mall END

			this._init = true;
		}
	}

	/**
	 * 创建SslContext对象，用于开启SSL/TLS加密传输。
	 *
	 * @return 如果成功创建则返回SslContext对象，否则返回null
	 */
	public SslContext createSslContext() {
		SslContext sslContext = null;
		try {
			sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
			Log.d(TAG, "【IMCORE-TCP】已开启SSL/TLS加密(单向认证)，且sslContext创建成功。");
		} catch (Exception e) {
			Log.w(TAG, "【IMCORE-TCP】创建sslContext时出错，原因是：" + e.getMessage(), e);
		}

		return sslContext;
	}
	
	/**
	 * 释放IM框架所占用的资源，在退出登陆时请务必调用本方法，否则重
	 * 新登陆将不能正常实现（指APP进程不退出时切换账号这种情况）。
	 */
	public void releaseMobileIMSDK() {
		// 释放IM核心库资源
		ClientCoreSDK.getInstance().release();
		
		// 重置本类的初始化标识
	    this.resetInitFlag();
		
		// 清空设置的回调
		ClientCoreSDK.getInstance().setChatBaseEvent(null);
		ClientCoreSDK.getInstance().setChatMessageEvent(null);
		ClientCoreSDK.getInstance().setMessageQoSEvent(null);

	    // 清空本量中的关键全局变量
	    this.localUserInfo = null;
	    this.currentFrontGroupChattingGroupID = null;
	    this.currentFrontTempChattingUserUID = null;
	    this.currentFrontChattingUserUID = null;

	    // 重置关键数据模型变量
		this.friendsListProvider = null;
		this.groupsProvider = null;
	    this.messagesProvider = null;
	    this.groupsMessagesProvider = null;
		this.alarmsProvider = null;
		this.faceDataProvider = null;
		this.friendsReqProvider = null;
	    
	    this.currentVideoChattingActivity = null;

	    // 清空群聊中的刷屏检查
		ProhibitSpeechCheckerProvider.getInstance().clear();

		// 清空撤回消息管理器中的数据集合
		messageRevokingManager.clear();

		// 释放实时音视频相关资源
		MBThreadPoolExecutor.runInBackground(() -> VVP2PProvider.getInstance(applicationContext).release());
	}
	
	/**
	 * 重置本类的初始化标识。
	 */
	private void resetInitFlag()
	{
	    this._init = false;
	}
	
	public ChatMessageEventListener getTransDataListener()
	{
		return transDataListener;
	}
	
	public ChatBaseEventListener getBaseEventListener()
	{
		return baseEventListener;
	}
	
	public MessageQoSListener getMessageQoSListener()
	{
		return messageQoSListener;
	}

	public BigFileUploadManager getBigFileUploadManager() {
		return bigFileUploadManager;
	}

	public BigFileDownloadManager getBigFileDownloadManager() {
		return bigFileDownloadManager;
	}

	public MessageRevokingManager getMessageRevokingManager() {
		return messageRevokingManager;
	}

	// ******************************************************************* For Mall START
	@Override
	public MallGlobal getRobotimeMall() {
		return robotimeMall;
	}
	// ******************************************************************* For Mall END
	
	/**
	 * 获得当前正在聊天中的用户UID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link FriendChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link FriendChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）.
	 * 
	 * @return
	 */
	public String getCurrentFrontChattingUserUID()
	{
		return currentFrontChattingUserUID;
	}
	
	/**
	 * 设置当前正在聊天中的用户UID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link FriendChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link FriendChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）.
	 * 
	 * @param currentFrontChattingUserUID
	 */
	public void setCurrentFrontChattingUserUID(String currentFrontChattingUserUID) {
		this.currentFrontChattingUserUID = currentFrontChattingUserUID;
	}
	
	/**
	 * 获得当前正在临时聊天中的用户UID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link TempChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link TempChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）.
	 * 
	 * @return
	 */
	public String getCurrentFrontTempChattingUserUID() {
		return currentFrontTempChattingUserUID;
	}
	
	/**
	 * 设置当前正在临时聊天中的用户UID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link TempChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link TempChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）.
	 * 
	 * @param currentFrontTempChattingUserUID
	 */
	public void setCurrentFrontTempChattingUserUID(String currentFrontTempChattingUserUID) {
		this.currentFrontTempChattingUserUID = currentFrontTempChattingUserUID;
	}
	
	/**
	 * 获得当前正在群聊聊天中的聊天群ID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link GroupChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link TempChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）.
	 *
	 * @return
	 */
	public String getCurrentFrontGroupChattingGroupID() {
		return currentFrontGroupChattingGroupID;
	}

	/**
	 * 设置当前正在群聊聊天中的聊天群ID.
	 * <p>
	 * <b>重要说明：</b>此变量只在{@link GroupChattingActivity}处于前景（即在onResume()方法调用的情况下）被
	 * 设置、在{@link TempChattingActivity}处于非激活或关闭（即在onPause()方法调用的情况下）被取消设置（置成null）.
	 *
	 * @param currentFrontGroupChattingGroupID
	 */
	public void setCurrentFrontGroupChattingGroupID(String currentFrontGroupChattingGroupID) {
		this.currentFrontGroupChattingGroupID = currentFrontGroupChattingGroupID;
	}

	public boolean isNeedRefreshPhotoListForUpdate()
	{
		return needRefreshPhotoListForUpdate;
	}

	public void setNeedRefreshPhotoListForUpdate(boolean needRefreshPhotoListForUpdate) {
		this.needRefreshPhotoListForUpdate = needRefreshPhotoListForUpdate;
	}
	
	public RosterElementEntity getLocalUserInfo()
	{
		return localUserInfo;
	}
	
	public void setLocalUserInfo(RosterElementEntity localUserInfo)
	{
		this.localUserInfo = localUserInfo;
	}

	public static String getTokenSafe() {
		try{
			RosterElementEntity localUserInfo = MyApplication.getInstance2().getIMClientManager().getLocalUserInfo();
			if(localUserInfo != null) {
				return localUserInfo.getToken();
			}
		} catch (Exception e) {
			Log.w(TAG, e);
		}

		return null;
	}

	/**
	 * 是本地用户。
	 *
	 * @param uid 用户uid
	 * @return true表示是本地用户，否则不是
	 */
	public boolean isLocalUser(String uid){
		RosterElementEntity localUserInfo = this.getLocalUserInfo();
		return (localUserInfo != null && uid != null && uid.equals(localUserInfo.getUser_uid()));
	}

	public FriendsListProvider getFriendsListProvider() {
		return friendsListProvider;
	}

	public GroupsProvider getGroupsProvider() {
		return groupsProvider;
	}

	public AlarmsProvider getAlarmsProvider() {
		return alarmsProvider;
	}
	
	public MessagesProvider getMessagesProvider() {
		return messagesProvider;
	}
	
	public GroupsMessagesProvider getGroupsMessagesProvider() {
		return groupsMessagesProvider;
	}

	public FaceDataProvider getFaceDataProvider() {
		if(this.faceDataProvider == null){
			this.faceDataProvider = new FaceDataProvider(applicationContext);
		}
		return faceDataProvider;
	}

	public FriendsReqProvider getFriendsReqProvider() {
		return this.friendsReqProvider;
	}
	
	public IVideoActivity getCurrentVideoChattingActivity() {
		return currentVideoChattingActivity;
	}

	public void setCurrentVideoChattingActivity(IVideoActivity currentVideoChattingActivity) {
		this.currentVideoChattingActivity = currentVideoChattingActivity;
	}

	/**
	 * 退出IM服务器连接并释放IM所占的所有资源（含退出IM框架连接、再释放IM框架所占的资源）。
	 * <p>
	 * 在切换账号等功能场景下，使用本方法可以保证重新登陆时IM框架
	 * 已回到重初状态，从而完全正常的重新登陆。
	 * 
	 * @see LocalDataSender#sendLoginout()
	 * @see IMClientManager#releaseMobileIMSDK()
	 */
	public static void doLogoutIMServer(final Context context, final Observer obsForSucess) {
		// 退出登陆
		new AsyncTask<Object, Integer, Integer>(){
			private Observer obsForSucess = null;
			
			@Override
			protected Integer doInBackground(Object... params) {
				int code = -1;
				try {
					if(params != null)
						obsForSucess = (Observer)params[0];
					
					// local user info 为空即意味着用户极是打开了登陆界面而尚未有登陆过，那当然就不需要退出登陆罗
					if(MyApplication.getInstance(context).getIMClientManager().getLocalUserInfo() != null)
						code = LocalDataSender.getInstance().sendLoginout();
				}
				catch (Exception e) {
					Log.e(TAG, "错误发生于doLogout:sendLoginout时："+e.getMessage(), e);
				}
				return code;
			}

			@Override
			protected void onPostExecute(Integer code) {
				// local user info 为空即意味着用户极是打开了登陆界面而尚未有登陆过，那当然就不需要退出登陆罗
				if(MyApplication.getInstance(context).getIMClientManager().getLocalUserInfo() != null) {
					if(code == 0)
						Log.d(LoginActivity.class.getSimpleName(), "注销登陆请求已成功发出！");
					else
						Log.w(LoginActivity.class.getSimpleName(), "注销登陆请求发送失败。错误码是："+code+"！");
				}
				
				try {
					// 释放IM所占资源
					MyApplication.getInstance(context).getIMClientManager().releaseMobileIMSDK();
				}
				catch (Exception e) {
					Log.e(TAG, "错误发生于logoutMobileIMSDK:onPostExecute:releaseMobileIMSDK()时："+e.getMessage(), e);
				}
				
				// * 处理完成后通知观察者做其它事哦
				if(obsForSucess != null)
					obsForSucess.update(null, null);
			}
		}.execute(obsForSucess);
	}
}
